using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Caching;
using ShrinkWrap.Minifier;
using ShrinkWrap.Exceptions;
using ShrinkWrap.Configuration;
using System.Configuration;
using System.IO.Compression;

namespace ShrinkWrap.HttpHandler
{
    /// <summary>
    /// Handles HTTP requests using a Minifier and Content Type defined by concrete subclasses.
    /// </summary>
    public abstract class MinifyingHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            var response = new HttpResponseWrapper(context.Response);
            var request = new HttpRequestWrapper(context.Request.Url, context.Request.Headers, context.Request.HttpMethod);
            ProcessAggregation(request, response, context.Request.MapPath, context.Cache);
        }

        public void ProcessAggregation(IHttpRequest request, IHttpResponse response, Func<String, String> mapPath, Cache cache)
        {
            try
            {
                if (request.Verb == "GET" || request.Verb == "HEAD")
                {
                    var compositeUri = new CompositeUri(request.Uri);
                    Aggregator aggregator = GetAggregator(mapPath, cache, compositeUri);

                    // Add headers
                    encode(request.Headers, response);
                    response.Expires = config.Expiry;
                    response.ContentType = ContentType;

                    // Serve the result
                    if (request.Verb != "HEAD")
                    {
                        response.Write(aggregator.GetAggregate());
                    }
                }
                else
                {
                    response.StatusCode = 405;
                }
            }
            catch (ShrinkWrapException)
            {
                // We are unwilling or unable to satisfy the request.
                response.StatusCode = 404;
            }
            catch (Exception)
            {
                // Handle all other errors by returning a generic server error code.
                response.StatusCode = 500;
            }
        }

        private static void encode(NameValueCollection requestHeaders, IHttpResponse response)
        {
            var acceptEncoding = requestHeaders["Accept-Encoding"];
            var requestEncodings = acceptEncoding != null ? acceptEncoding.Split(new Char[] { ',' }) : new String[] { };
            var gzipRequested = requestEncodings.Contains("gzip");
            if (gzipRequested)
            {
                response.AddHeader("Content-Encoding", "gzip");
                response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
            }

            // Regardless of whether or not we're compressing, we need to indicate to caches that we might be.
            response.AddHeader("Vary", "Accept-Encoding");
        }

        public virtual Aggregator GetAggregator(Func<String, String> mapPath, Cache cache, CompositeUri compositeUri)
        {
            Aggregator aggregator = new CachingAggregator(Minifier, compositeUri, mapPath, config.MaxConstituents, cache);
            return aggregator;
        }

        /// <summary>
        /// Used to minify the output before it is sent to the client.
        /// </summary>
        public abstract IMinifier Minifier { get; }

        /// <summary>
        /// The value of the Content Type HTTP response header.
        /// </summary>
        public abstract String ContentType { get; }

        public bool IsReusable
        {
            get
            {
                return true;
            }
        }

        public ShrinkWrapConfiguration config = ShrinkWrapConfiguration.Get();
    }
}
